Candace Savonen - CCDL for ALSF

This notebook is a first pass in evaluating the concordance between MuTect2 and Strelka2 results.

It addresses issue # 30 in OpenPBTA.

Set Up

* Some of these set up steps will be removed once we have a Dockerfile that installs maftools automatically.

if (!('colorblindr' %in% installed.packages())) {
devtools::install_github("clauswilke/colorblindr")
}
Downloading GitHub repo clauswilke/colorblindr@master
Installing 6 packages: cowplot, shiny, httpuv, sourcetools, later, promises
cannot open URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/PACKAGES.rds': HTTP status was '404 Not Found'trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/cowplot_1.0.0.tgz'
Content type 'application/x-gzip' length 1362732 bytes (1.3 MB)
==================================================
downloaded 1.3 MB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/shiny_1.3.2.tgz'
Content type 'application/x-gzip' length 4655011 bytes (4.4 MB)
==================================================
downloaded 4.4 MB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/httpuv_1.5.1.tgz'
Content type 'application/x-gzip' length 2936016 bytes (2.8 MB)
==================================================
downloaded 2.8 MB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/sourcetools_0.1.7.tgz'
Content type 'application/x-gzip' length 138829 bytes (135 KB)
==================================================
downloaded 135 KB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/later_0.8.0.tgz'
Content type 'application/x-gzip' length 385957 bytes (376 KB)
==================================================
downloaded 376 KB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/promises_1.0.1.tgz'
Content type 'application/x-gzip' length 307177 bytes (299 KB)
==================================================
downloaded 299 KB

The downloaded binary packages are in
    /var/folders/1c/kxsx50393_g8qkhd83sktgm80000gn/T//RtmpQ451K5/downloaded_packages
  
   checking for file ‘/private/var/folders/1c/kxsx50393_g8qkhd83sktgm80000gn/T/RtmpQ451K5/remotesda2b47fcecea/clauswilke-colorblindr-1ac3d4d/DESCRIPTION’ ...
  
✔  checking for file ‘/private/var/folders/1c/kxsx50393_g8qkhd83sktgm80000gn/T/RtmpQ451K5/remotesda2b47fcecea/clauswilke-colorblindr-1ac3d4d/DESCRIPTION’

  
─  preparing ‘colorblindr’:

  
   checking DESCRIPTION meta-information ...
  
✔  checking DESCRIPTION meta-information

  
─  checking for LF line-endings in source and make files and shell scripts

  
─  checking for empty or unneeded directories

  
─  building ‘colorblindr_0.1.0.tar.gz’

  
   


   File /Users/candacesavonen/.Renviron contains invalid line(s)
      GITHUB_PATbb0bf7f0c887a3d0e5b68ddb8749bb30986ca69f
   They were ignored
* installing *source* package ‘colorblindr’ ...
** using staged installation
** R
** inst
** byte-compile and prepare package for lazy loading

   File /Users/candacesavonen/.Renviron contains invalid line(s)
      GITHUB_PATbb0bf7f0c887a3d0e5b68ddb8749bb30986ca69f
   They were ignored
** help
*** installing help indices
*** copying figures
** building package indices
** installing vignettes
** testing if installed package can be loaded from temporary location

   File /Users/candacesavonen/.Renviron contains invalid line(s)
      GITHUB_PATbb0bf7f0c887a3d0e5b68ddb8749bb30986ca69f
   They were ignored
** testing if installed package can be loaded from final location

   File /Users/candacesavonen/.Renviron contains invalid line(s)
      GITHUB_PATbb0bf7f0c887a3d0e5b68ddb8749bb30986ca69f
   They were ignored
** testing if installed package keeps a record of temporary installation path
* DONE (colorblindr)

Create a output directories.

if (!dir.exists("results")) {
  dir.create("results")
}
if (!dir.exists("plots")) {
  dir.create("plots")
}

Read in the metadata information

This will be changed once the data download script is finalized.

Prep the metadata to be used as the clinicalData for maftools it it hasn’t been prepped yet. This whole chunk may need to be removed be taken out after issue 2 regarding the data download script is addressed.

These steps are only to make the metadata and MuTect2 and Strelka2 datasets parallel in what samples they contain.
There are some samples in the metadata that are not in the MuTect2 and Strelka2 data.

if (file.exists(file.path("..", "..", "scratch", "metadata_filtered.tsv"))) {
  metadata <- readr::read_tsv(file.path("..", "..", "scratch", "metadata_filtered.tsv"))
}
Parsed with column specification:
cols(
  sample_id = col_character(),
  patient_id = col_character(),
  sample_type = col_character(),
  Composition = col_character(),
  disease_type = col_character(),
  `Tumor Descriptor` = col_character(),
  primary_site = col_character(),
  gender = col_character(),
  race = col_character(),
  ethnicity = col_character(),
  age_at_diagnosis = col_double()
)

Read in the Strelka2 and Mutect2 data

Will read in as an maftools object from an RDS file, unless maftools has not been run on them yet. Running maftools takes a lot of computing power and time, so be prepared. If you trying to run this step in a Docker container, it will likely be out of memory killed, unless you have ~50GB you can allot to Docker.

# Load in the RDS file if it exists, but if it doesn't exist, load in from 
# original data file with maftools
if (!file.exists(file.path("..", "..", "scratch", "strelka2.RDS"))) {
  strelka <- maftools::read.maf(file.path("..", "..", "data", "strelka2.maf.gz"),
                                clinicalData = metadata)
  saveRDS(strelka, file.path("..", "..", "scratch", "strelka2.RDS"))
} else {
  strelka2 <- readRDS(file.path("..", "..", "scratch", "strelka2.RDS"))
}

This is how I set up metadata_filtered.tsv. This should not need to be ran again and won’t run again unless metadata_filtered.tsv is misplaced from the scratch file.

if (!file.exists(file.path("..", "..", "scratch", "metadata_filtered.tsv"))) {
  # Isolate metadata to only the samples that are in the datasets
  metadata <- metadata %>%
    dplyr::filter(sample_id %in% mutect2@clinical.data$Tumor_Sample_Barcode) %>%
    dplyr::distinct(sample_id, .keep_all = TRUE) %>%
    readr::write_tsv(file.path("..", "..", "scratch", "metadata_filtered.tsv"))
}

Check that samples are same order in the datasets as they are in the metadata

all.equal(as.factor(metadata$sample_id),
          strelka2@clinical.data$Tumor_Sample_Barcode)
[1] TRUE
all.equal(as.factor(metadata$sample_id), 
          mutect2@clinical.data$Tumor_Sample_Barcode)
[1] TRUE

Get summaries and write them to TSVs

Get gene summaries and write to TSV files.

strelka2_gene_sum <- maftools::getGeneSummary(strelka2) %>% 
  readr::write_tsv(file.path("results", 
                             "strelka2_gene_summary.tsv"))

mutect2_gene_sum <- maftools::getGeneSummary(mutect2) %>% 
  readr::write_tsv(file.path("results", 
                             "mutect2_gene_summary.tsv"))

Get sample summaries and write to TSV files.

strelka2_sample_sum <- maftools::getSampleSummary(strelka2) %>% 
  readr::write_tsv(file.path("results", 
                             "strelka2_sample_summary.tsv"))

mutect2_sample_sum <- maftools::getSampleSummary(mutect2) %>% 
  readr::write_tsv(file.path("results", 
                             "mutect2_sample_summary.tsv"))

Number of mutations per gene correlation

combined_gene <- mutect2_gene_sum %>% 
  dplyr::full_join(strelka2_gene_sum, by = 'Hugo_Symbol') %>%
  reshape2::melt(id = 'Hugo_Symbol') %>% 
  dplyr::mutate(dataset = as.character(grepl(".x$", variable))) %>%
  dplyr::mutate(dataset = dplyr::recode(dataset, 
                                        `TRUE` = "mutect2", 
                                        `FALSE` = "strelka2")) %>%
  dplyr::mutate(variable = gsub(".x$|.y$", "", variable)) %>% 
  tidyr::spread('dataset', 'value')

Make number of mutations per gene scatterplots.

gene_cor <- ggplot2::ggplot(combined_gene, ggplot2::aes(x = mutect2, y = strelka2)) +
  ggplot2::geom_hex(bins = 10) + 
  ggplot2::facet_wrap(~variable, scales = "free") + 
  ggplot2::geom_smooth(method = lm) + 
  ggplot2::theme_classic() + 
  ggplot2::xlab("Mutect2: Number of mutations per gene") +
  ggplot2::ylab("Strelka2: Number of mutations per gene") 

# Print out the plot in this notebook
gene_cor

Save the plot to a PDF.

ggplot2::ggsave(file.path("plots", "gene_cor_mutect2_vs_strelka2.pdf"))
Saving 7 x 7 in image

Let’s get a correlation test on the genes overall.

cor.test(combined_gene$mutect2, combined_gene$strelka2, method = "spearman")
Cannot compute exact p-value with ties

    Spearman's rank correlation rho

data:  combined_gene$mutect2 and combined_gene$strelka2
S = 4.2762e+13, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.9604808 
cor.test(combined_gene$mutect2, combined_gene$strelka2, method = "pearson")

    Pearson's product-moment correlation

data:  combined_gene$mutect2 and combined_gene$strelka2
t = 580.42, df = 186550, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.8006256 0.8038602
sample estimates:
      cor 
0.8022488 

Number of mutations per sample correlation.

combined_sample <- mutect2_sample_sum %>% 
  dplyr::full_join(strelka2_sample_sum, by = 'Tumor_Sample_Barcode') %>%
  reshape2::melt(id = 'Tumor_Sample_Barcode') %>% 
  dplyr::mutate(dataset = as.character(grepl(".x$", variable))) %>%
  dplyr::mutate(dataset = dplyr::recode(dataset, 
                                        `TRUE` = "mutect2", 
                                        `FALSE` = "strelka2")) %>%
  dplyr::mutate(variable = gsub(".x$|.y$", "", variable)) %>% 
  tidyr::spread('dataset', 'value')

Make number of mutations per sample scatterplots.

sample_cor <- ggplot2::ggplot(combined_sample, ggplot2::aes(x = mutect2, y = strelka2)) +
  ggplot2::geom_hex(bins = 10) + 
  ggplot2::facet_wrap(~variable, scales = "free") + 
  ggplot2::geom_smooth(method = lm) + 
  ggplot2::theme_classic() +
  ggplot2::xlab("Mutect2: Number of mutations per sample") +
  ggplot2::ylab("Strelka2: Number of mutations per sample") 

# Print out the plot in this notebook
sample_cor

Save the plot to a PDF.

ggplot2::ggsave(file.path("plots", "sample_cor_mutect2_vs_strelka2.pdf"))
Saving 7 x 7 in image

Let’s get a correlation test on the genes overall. Question 2) Is this a reasonable amount of concordance for these methods across samples?

cor.test(combined_sample$mutect2, combined_sample$strelka2, method = "spearman")
Cannot compute exact p-value with ties

    Spearman's rank correlation rho

data:  combined_sample$mutect2 and combined_sample$strelka2
S = 2.8267e+10, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.7749395 
cor.test(combined_sample$mutect2, combined_sample$strelka2, method = "pearson")

    Pearson's product-moment correlation

data:  combined_sample$mutect2 and combined_sample$strelka2
t = 720.76, df = 9098, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.9909958 0.9917034
sample estimates:
      cor 
0.9913568 

Calculate VAF

Set up data.frame with mutation and VAF for Strelka2.

strelka2_vaf <- strelka2@data %>%
  dplyr::filter(!grepl("-", Allele)) %>%
  dplyr::mutate(vaf = as.numeric(t_alt_count)/(as.numeric(t_ref_count) + 
                                                 as.numeric(t_alt_count)),
                mutation = paste0(Hugo_Symbol, "_", 
                                  Allele, "_", 
                                  Tumor_Sample_Barcode, "_", 
                                  Start_Position), 
                change = paste0(Reference_Allele, ">", Allele),
                coding = dplyr::case_when( 
                  BIOTYPE != "protein_coding" ~ "non-coding",
                  TRUE ~ "protein_coding")) %>%
    dplyr::select(-which(apply(is.na(.), 2, all)))
NAs introduced by coercion
# Take a look at this df
strelka2_vaf

Set up data.frame with mutation and VAF for MuTect2.

mutect2_vaf <- mutect2@data %>%
  dplyr::filter(!grepl("-", Allele)) %>%
  dplyr::mutate(vaf = as.numeric(t_alt_count)/(as.numeric(t_ref_count) + 
                                                 as.numeric(t_alt_count)),
                mutation = paste0(Hugo_Symbol, "_", 
                                  Allele, "_", 
                                  Tumor_Sample_Barcode, "_", 
                                  Start_Position),
                change = paste0(Reference_Allele, ">", Allele), 
                coding = dplyr::case_when( 
                  BIOTYPE != "protein_coding" ~ "non-coding",
                  TRUE ~ "protein_coding")) %>%
  dplyr::select(-which(apply(is.na(.), 2, all)))

# Take a look at this df
mutect2_vaf

Combine MuTect2 and Strelka2 VAF data.frames so we can compare.

# Merge these data.frames together
vaf_df <- strelka2_vaf %>%
  dplyr::full_join(mutect2_vaf, by = 'mutation', 
                    suffix = c(".strelka2", ".mutect2")) %>%
  # Make a variable that denotes which dataset it is in.
  dplyr::mutate(dataset = dplyr::case_when( 
    is.na(vaf.mutect2) ~ "strelka2_only",
    is.na(vaf.strelka2) ~ "mutect2_only", 
    TRUE ~ "both")) 

Plot this as a scatterplot

vaf_df %>%
ggplot2::ggplot(ggplot2::aes(x = vaf.strelka2, y = vaf.mutect2)) + 
  ggplot2::geom_hex() +
  ggplot2::theme_classic() + 
  ggplot2::xlab("VAF for each mutation for Strelka2") + 
  ggplot2::ylab("VAF for each mutation for MuTect2") 

vaf_df %>%
  tidyr::gather(key = "data", value = "vaf" , vaf.strelka2, vaf.mutect2) %>%
  dplyr::mutate(data = gsub("^vaf.", "", data)) %>% 
  dplyr::mutate(data.group = paste(dataset, ":", data, "VAF")) %>%
  dplyr::filter(!is.na(vaf)) %>%
# Plot it
ggplot2::ggplot(ggplot2::aes(data.group, vaf)) + 
  ggplot2::geom_violin(fill = "light blue") +
  ggplot2::theme_classic( ) + 
  ggplot2::ylab("Density of VAF") + 
  ggplot2::xlab(" ")

Venn Diagrams

Make the Venn diagram of MuTect2 and Strelka2 mutations.

count <- summary(as.factor(vaf_df$dataset))

# Take a look at this summary
count
         both  mutect2_only strelka2_only 
        62181         23121         35012 
# Make the Venn diagram
grid::grid.newpage();
venn.plot <- VennDiagram::draw.pairwise.venn(
  area1 = count[3] + count[1],
  area2 = count[2] + count[1],
  cross.area = count[1],
  category = c("Strelka2", "MuTect2"),
  fill = c("blue", "yellow"),
  cex = 2,
  cat.cex = 1.5,
  cat.dist = c(-0.04, -0.031),
  ext.pos = 0,
  ext.dist = -0.01,
  ext.length = .8,
  ext.line.lwd = 2,
  ext.line.lty = "dashed");
grid::grid.draw(venn.plot) # Draw plot

Save the Venn Diagram plot to a PDF.

# Make filename to save plot as
venn.plot.file <- file.path("plots", 
                            "strelka2_mutect2_venn_diagram.pdf")
pdf(venn.plot.file);
grid::grid.draw(venn.plot);
dev.off()
null device 
          1 

What types of variants are are the most discrepant between the algorithms?

Let’s make a wrapper function that will do this for whatever variables we are interested in.

barplot_var(vaf_df, "change", 100)

barplot_var(vaf_df, "coding", 10)

barplot_var("Variant_Classification", 10)

Session Info:

sessionInfo()
LS0tCnRpdGxlOiAiRXZhbHVhdGUgY29uY29yZGFuY2UgYmV0d2VlbiBNdXRlY3QyIGFuZCBTdHJlbGthMiIKb3V0cHV0OiAgIAogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgpDYW5kYWNlIFNhdm9uZW4gLSBDQ0RMIGZvciBBTFNGCgpUaGlzIG5vdGVib29rIGlzIGEgZmlyc3QgcGFzcyBpbiBldmFsdWF0aW5nIHRoZSBjb25jb3JkYW5jZSBiZXR3ZWVuIE11VGVjdDIgYW5kIApTdHJlbGthMiByZXN1bHRzLgoKSXQgYWRkcmVzc2VzIFtpc3N1ZSBcIyAzMCBpbiBPcGVuUEJUQV0oaHR0cHM6Ly9naXRodWIuY29tL0FsZXhzTGVtb25hZGUvT3BlblBCVEEtYW5hbHlzaXMvaXNzdWVzLzMwKS4KCiMjIFNldCBVcAoKXCogU29tZSBvZiB0aGVzZSBzZXQgdXAgc3RlcHMgd2lsbCBiZSByZW1vdmVkIG9uY2Ugd2UgaGF2ZSBhIERvY2tlcmZpbGUgdGhhdCBpbnN0YWxscwptYWZ0b29scyBhdXRvbWF0aWNhbGx5LiAKCmBgYHtyfQojIFdlIG5lZWQgbWFmdG9vbHMgLSB0aGlzIHdpbGwgYmUgYWRkZWQgdG8gdGhlIHJ1bm5pbmcgRG9ja2VyIGlzc3VlIHdoZW5ldmVyIGl0IGlzIHVwCmlmICghKCdtYWZ0b29scycgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSkpIHsKICBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIlBvaXNvbkFsaWVuL21hZnRvb2xzIikKfQoKaWYgKCEoJ2hleGJpbicgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSkpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJoZXhiaW4iKQp9CgppZiAoISgnY29sb3JibGluZHInICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpKSB7CiAgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJjbGF1c3dpbGtlL2NvbG9yYmxpbmRyIikKfQojIEdldCBtYWdyaXR0ciBwaXBlCmAlPiVgIDwtIGRwbHlyOjpgJT4lYApgYGAKCkNyZWF0ZSBhIG91dHB1dCBkaXJlY3Rvcmllcy4KCmBgYHtyfQppZiAoIWRpci5leGlzdHMoInJlc3VsdHMiKSkgewogIGRpci5jcmVhdGUoInJlc3VsdHMiKQp9CmlmICghZGlyLmV4aXN0cygicGxvdHMiKSkgewogIGRpci5jcmVhdGUoInBsb3RzIikKfQpgYGAKCiMjIFJlYWQgaW4gdGhlIG1ldGFkYXRhIGluZm9ybWF0aW9uClRoaXMgd2lsbCBiZSBjaGFuZ2VkIG9uY2UgdGhlIFtkYXRhIGRvd25sb2FkIHNjcmlwdF0oaHR0cHM6Ly9naXRodWIuY29tL0FsZXhzTGVtb25hZGUvT3BlblBCVEEtYW5hbHlzaXMvaXNzdWVzLzIpIGlzIGZpbmFsaXplZC4gCgpQcmVwIHRoZSBtZXRhZGF0YSB0byBiZSB1c2VkIGFzIHRoZSBgY2xpbmljYWxEYXRhYCBmb3IgbWFmdG9vbHMgaXQgaXQgaGFzbid0IGJlZW4gCnByZXBwZWQgeWV0LiAKVGhpcyB3aG9sZSBjaHVuayBtYXkgbmVlZCB0byBiZSByZW1vdmVkIGJlIHRha2VuIG91dCBhZnRlciBpc3N1ZSAyIHJlZ2FyZGluZyB0aGUgW2RhdGEgZG93bmxvYWQgc2NyaXB0XShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9PcGVuUEJUQS1hbmFseXNpcy9pc3N1ZXMvMikgaXMgYWRkcmVzc2VkLiAKClRoZXNlIHN0ZXBzIGFyZSBvbmx5IHRvIG1ha2UgdGhlIG1ldGFkYXRhIGFuZCBNdVRlY3QyIGFuZCBTdHJlbGthMiBkYXRhc2V0cyAKcGFyYWxsZWwgaW4gd2hhdCBzYW1wbGVzIHRoZXkgY29udGFpbi4gIApUaGVyZSBhcmUgc29tZSBzYW1wbGVzIGluIHRoZSBtZXRhZGF0YSB0aGF0IGFyZSBub3QgaW4gdGhlIE11VGVjdDIgYW5kIFN0cmVsa2EyCmRhdGEuIAoKYGBge3J9CmlmIChmaWxlLmV4aXN0cyhmaWxlLnBhdGgoIi4uIiwgIi4uIiwgInNjcmF0Y2giLCAibWV0YWRhdGFfZmlsdGVyZWQudHN2IikpKSB7CiAgbWV0YWRhdGEgPC0gcmVhZHI6OnJlYWRfdHN2KGZpbGUucGF0aCgiLi4iLCAiLi4iLCAic2NyYXRjaCIsICJtZXRhZGF0YV9maWx0ZXJlZC50c3YiKSkKfQpgYGAKCiMjIFJlYWQgaW4gdGhlIFN0cmVsa2EyIGFuZCBNdXRlY3QyIGRhdGEKCldpbGwgcmVhZCBpbiBhcyBhbiBgbWFmdG9vbHNgIG9iamVjdCBmcm9tIGFuIFJEUyBmaWxlLCB1bmxlc3MgYG1hZnRvb2xzYCBoYXMgbm90IGJlZW4gCnJ1biBvbiB0aGVtIHlldC4KUnVubmluZyBgbWFmdG9vbHNgIHRha2VzIGEgbG90IG9mIGNvbXB1dGluZyBwb3dlciBhbmQgdGltZSwgc28gYmUgcHJlcGFyZWQuCklmIHlvdSB0cnlpbmcgdG8gcnVuIHRoaXMgc3RlcCBpbiBhIERvY2tlciBjb250YWluZXIsIGl0IHdpbGwgbGlrZWx5IGJlIG91dCBvZiAKbWVtb3J5IGtpbGxlZCwgdW5sZXNzIHlvdSBoYXZlIH41MEdCIHlvdSBjYW4gYWxsb3QgdG8gRG9ja2VyLiAKCmBgYHtyfQojIExvYWQgaW4gdGhlIFJEUyBmaWxlIGlmIGl0IGV4aXN0cywgYnV0IGlmIGl0IGRvZXNuJ3QgZXhpc3QsIGxvYWQgaW4gZnJvbSAKIyBvcmlnaW5hbCBkYXRhIGZpbGUgd2l0aCBtYWZ0b29scwppZiAoIWZpbGUuZXhpc3RzKGZpbGUucGF0aCgiLi4iLCAiLi4iLCAic2NyYXRjaCIsICJzdHJlbGthMi5SRFMiKSkpIHsKICBzdHJlbGthIDwtIG1hZnRvb2xzOjpyZWFkLm1hZihmaWxlLnBhdGgoIi4uIiwgIi4uIiwgImRhdGEiLCAic3RyZWxrYTIubWFmLmd6IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxEYXRhID0gbWV0YWRhdGEpCiAgc2F2ZVJEUyhzdHJlbGthLCBmaWxlLnBhdGgoIi4uIiwgIi4uIiwgInNjcmF0Y2giLCAic3RyZWxrYTIuUkRTIikpCn0gZWxzZSB7CiAgc3RyZWxrYTIgPC0gcmVhZFJEUyhmaWxlLnBhdGgoIi4uIiwgIi4uIiwgInNjcmF0Y2giLCAic3RyZWxrYTIuUkRTIikpCn0KCiMgU2FtZSBmb3IgTXVUZWN0MgppZiAoIWZpbGUuZXhpc3RzKGZpbGUucGF0aCgiLi4iLCAiLi4iLCAic2NyYXRjaCIsICJtdXRlY3QyLlJEUyIpKSkgewogIG11dGVjdDIgPC0gbWFmdG9vbHM6OnJlYWQubWFmKGZpbGUucGF0aCgiLi4iLCAiLi4iLCAiZGF0YSIsICJtdXRlY3QyLm1hZi5neiIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGluaWNhbERhdGEgPSBtZXRhZGF0YSkKICBzYXZlUkRTKG11dGVjdDIsIGZpbGUucGF0aCgiLi4iLCAiLi4iLCAic2NyYXRjaCIsICJtdXRlY3QyLlJEUyIpKQp9IGVsc2UgewogIG11dGVjdDIgPC0gcmVhZFJEUyhmaWxlLnBhdGgoIi4uIiwgIi4uIiwgInNjcmF0Y2giLCAibXV0ZWN0Mi5SRFMiKSkKfQpgYGAKClRoaXMgaXMgaG93IEkgc2V0IHVwIGBtZXRhZGF0YV9maWx0ZXJlZC50c3ZgLiAKVGhpcyBzaG91bGQgbm90IG5lZWQgdG8gYmUgcmFuIGFnYWluIGFuZCB3b24ndCBydW4gYWdhaW4gdW5sZXNzIApgbWV0YWRhdGFfZmlsdGVyZWQudHN2YCBpcyBtaXNwbGFjZWQgZnJvbSB0aGUgc2NyYXRjaCBmaWxlLiAKCmBgYHtyfQppZiAoIWZpbGUuZXhpc3RzKGZpbGUucGF0aCgiLi4iLCAiLi4iLCAic2NyYXRjaCIsICJtZXRhZGF0YV9maWx0ZXJlZC50c3YiKSkpIHsKICAjIElzb2xhdGUgbWV0YWRhdGEgdG8gb25seSB0aGUgc2FtcGxlcyB0aGF0IGFyZSBpbiB0aGUgZGF0YXNldHMKICBtZXRhZGF0YSA8LSBtZXRhZGF0YSAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoc2FtcGxlX2lkICVpbiUgbXV0ZWN0MkBjbGluaWNhbC5kYXRhJFR1bW9yX1NhbXBsZV9CYXJjb2RlKSAlPiUKICAgIGRwbHlyOjpkaXN0aW5jdChzYW1wbGVfaWQsIC5rZWVwX2FsbCA9IFRSVUUpICU+JQogICAgcmVhZHI6OndyaXRlX3RzdihmaWxlLnBhdGgoIi4uIiwgIi4uIiwgInNjcmF0Y2giLCAibWV0YWRhdGFfZmlsdGVyZWQudHN2IikpCn0KYGBgCgpDaGVjayB0aGF0IHNhbXBsZXMgYXJlIHNhbWUgb3JkZXIgaW4gdGhlIGRhdGFzZXRzIGFzIHRoZXkgYXJlIGluIHRoZSBtZXRhZGF0YQoKYGBge3J9CmFsbC5lcXVhbChhcy5mYWN0b3IobWV0YWRhdGEkc2FtcGxlX2lkKSwKICAgICAgICAgIHN0cmVsa2EyQGNsaW5pY2FsLmRhdGEkVHVtb3JfU2FtcGxlX0JhcmNvZGUpCgphbGwuZXF1YWwoYXMuZmFjdG9yKG1ldGFkYXRhJHNhbXBsZV9pZCksIAogICAgICAgICAgbXV0ZWN0MkBjbGluaWNhbC5kYXRhJFR1bW9yX1NhbXBsZV9CYXJjb2RlKQpgYGAKCiMjIEdldCBzdW1tYXJpZXMgYW5kIHdyaXRlIHRoZW0gdG8gVFNWcyAKCkdldCBnZW5lIHN1bW1hcmllcyBhbmQgd3JpdGUgdG8gVFNWIGZpbGVzLiAKCmBgYHtyfQpzdHJlbGthMl9nZW5lX3N1bSA8LSBtYWZ0b29sczo6Z2V0R2VuZVN1bW1hcnkoc3RyZWxrYTIpICU+JSAKICByZWFkcjo6d3JpdGVfdHN2KGZpbGUucGF0aCgicmVzdWx0cyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJlbGthMl9nZW5lX3N1bW1hcnkudHN2IikpCgptdXRlY3QyX2dlbmVfc3VtIDwtIG1hZnRvb2xzOjpnZXRHZW5lU3VtbWFyeShtdXRlY3QyKSAlPiUgCiAgcmVhZHI6OndyaXRlX3RzdihmaWxlLnBhdGgoInJlc3VsdHMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibXV0ZWN0Ml9nZW5lX3N1bW1hcnkudHN2IikpCmBgYAoKR2V0IHNhbXBsZSBzdW1tYXJpZXMgYW5kIHdyaXRlIHRvIFRTViBmaWxlcy4gCgpgYGB7cn0Kc3RyZWxrYTJfc2FtcGxlX3N1bSA8LSBtYWZ0b29sczo6Z2V0U2FtcGxlU3VtbWFyeShzdHJlbGthMikgJT4lIAogIHJlYWRyOjp3cml0ZV90c3YoZmlsZS5wYXRoKCJyZXN1bHRzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmVsa2EyX3NhbXBsZV9zdW1tYXJ5LnRzdiIpKQoKbXV0ZWN0Ml9zYW1wbGVfc3VtIDwtIG1hZnRvb2xzOjpnZXRTYW1wbGVTdW1tYXJ5KG11dGVjdDIpICU+JSAKICByZWFkcjo6d3JpdGVfdHN2KGZpbGUucGF0aCgicmVzdWx0cyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJtdXRlY3QyX3NhbXBsZV9zdW1tYXJ5LnRzdiIpKQpgYGAKCiMjIE51bWJlciBvZiBtdXRhdGlvbnMgcGVyIGdlbmUgY29ycmVsYXRpb24gCgpgYGB7cn0KY29tYmluZWRfZ2VuZSA8LSBtdXRlY3QyX2dlbmVfc3VtICU+JSAKICBkcGx5cjo6ZnVsbF9qb2luKHN0cmVsa2EyX2dlbmVfc3VtLCBieSA9ICdIdWdvX1N5bWJvbCcpICU+JQogIHJlc2hhcGUyOjptZWx0KGlkID0gJ0h1Z29fU3ltYm9sJykgJT4lIAogIGRwbHlyOjptdXRhdGUoZGF0YXNldCA9IGFzLmNoYXJhY3RlcihncmVwbCgiLngkIiwgdmFyaWFibGUpKSkgJT4lCiAgZHBseXI6Om11dGF0ZShkYXRhc2V0ID0gZHBseXI6OnJlY29kZShkYXRhc2V0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBUUlVFYCA9ICJtdXRlY3QyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkFMU0VgID0gInN0cmVsa2EyIikpICU+JQogIGRwbHlyOjptdXRhdGUodmFyaWFibGUgPSBnc3ViKCIueCR8LnkkIiwgIiIsIHZhcmlhYmxlKSkgJT4lIAogIHRpZHlyOjpzcHJlYWQoJ2RhdGFzZXQnLCAndmFsdWUnKQpgYGAKCk1ha2UgbnVtYmVyIG9mIG11dGF0aW9ucyBwZXIgZ2VuZSBzY2F0dGVycGxvdHMuCgpgYGB7cn0KZ2VuZV9jb3IgPC0gZ2dwbG90Mjo6Z2dwbG90KGNvbWJpbmVkX2dlbmUsIGdncGxvdDI6OmFlcyh4ID0gbXV0ZWN0MiwgeSA9IHN0cmVsa2EyKSkgKwogIGdncGxvdDI6Omdlb21faGV4KGJpbnMgPSAxMCkgKyAKICBnZ3Bsb3QyOjpmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArIAogIGdncGxvdDI6Omdlb21fc21vb3RoKG1ldGhvZCA9IGxtKSArIAogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArIAogIGdncGxvdDI6OnhsYWIoIk11dGVjdDI6IE51bWJlciBvZiBtdXRhdGlvbnMgcGVyIGdlbmUiKSArCiAgZ2dwbG90Mjo6eWxhYigiU3RyZWxrYTI6IE51bWJlciBvZiBtdXRhdGlvbnMgcGVyIGdlbmUiKSAKCiMgUHJpbnQgb3V0IHRoZSBwbG90IGluIHRoaXMgbm90ZWJvb2sKZ2VuZV9jb3IKYGBgCgpTYXZlIHRoZSBwbG90IHRvIGEgUERGLgoKYGBge3J9CmdncGxvdDI6Omdnc2F2ZShmaWxlLnBhdGgoInBsb3RzIiwgImdlbmVfY29yX211dGVjdDJfdnNfc3RyZWxrYTIucGRmIikpCmBgYAoKTGV0J3MgZ2V0IGEgY29ycmVsYXRpb24gdGVzdCBvbiB0aGUgZ2VuZXMgb3ZlcmFsbC4KCmBgYHtyfQpjb3IudGVzdChjb21iaW5lZF9nZW5lJG11dGVjdDIsIGNvbWJpbmVkX2dlbmUkc3RyZWxrYTIsIG1ldGhvZCA9ICJzcGVhcm1hbiIpCmNvci50ZXN0KGNvbWJpbmVkX2dlbmUkbXV0ZWN0MiwgY29tYmluZWRfZ2VuZSRzdHJlbGthMiwgbWV0aG9kID0gInBlYXJzb24iKQpgYGAKCiMjIE51bWJlciBvZiBtdXRhdGlvbnMgcGVyIHNhbXBsZSBjb3JyZWxhdGlvbi4gCgpgYGB7cn0KY29tYmluZWRfc2FtcGxlIDwtIG11dGVjdDJfc2FtcGxlX3N1bSAlPiUgCiAgZHBseXI6OmZ1bGxfam9pbihzdHJlbGthMl9zYW1wbGVfc3VtLCBieSA9ICdUdW1vcl9TYW1wbGVfQmFyY29kZScpICU+JQogIHJlc2hhcGUyOjptZWx0KGlkID0gJ1R1bW9yX1NhbXBsZV9CYXJjb2RlJykgJT4lIAogIGRwbHlyOjptdXRhdGUoZGF0YXNldCA9IGFzLmNoYXJhY3RlcihncmVwbCgiLngkIiwgdmFyaWFibGUpKSkgJT4lCiAgZHBseXI6Om11dGF0ZShkYXRhc2V0ID0gZHBseXI6OnJlY29kZShkYXRhc2V0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBUUlVFYCA9ICJtdXRlY3QyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkFMU0VgID0gInN0cmVsa2EyIikpICU+JQogIGRwbHlyOjptdXRhdGUodmFyaWFibGUgPSBnc3ViKCIueCR8LnkkIiwgIiIsIHZhcmlhYmxlKSkgJT4lIAogIHRpZHlyOjpzcHJlYWQoJ2RhdGFzZXQnLCAndmFsdWUnKQpgYGAKCk1ha2UgbnVtYmVyIG9mIG11dGF0aW9ucyBwZXIgc2FtcGxlIHNjYXR0ZXJwbG90cy4KCmBgYHtyfQpzYW1wbGVfY29yIDwtIGdncGxvdDI6OmdncGxvdChjb21iaW5lZF9zYW1wbGUsIGdncGxvdDI6OmFlcyh4ID0gbXV0ZWN0MiwgeSA9IHN0cmVsa2EyKSkgKwogIGdncGxvdDI6Omdlb21faGV4KGJpbnMgPSAxMCkgKyAKICBnZ3Bsb3QyOjpmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArIAogIGdncGxvdDI6Omdlb21fc21vb3RoKG1ldGhvZCA9IGxtKSArIAogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6eGxhYigiTXV0ZWN0MjogTnVtYmVyIG9mIG11dGF0aW9ucyBwZXIgc2FtcGxlIikgKwogIGdncGxvdDI6OnlsYWIoIlN0cmVsa2EyOiBOdW1iZXIgb2YgbXV0YXRpb25zIHBlciBzYW1wbGUiKSAKCiMgUHJpbnQgb3V0IHRoZSBwbG90IGluIHRoaXMgbm90ZWJvb2sKc2FtcGxlX2NvcgpgYGAKClNhdmUgdGhlIHBsb3QgdG8gYSBQREYuCgpgYGB7cn0KZ2dwbG90Mjo6Z2dzYXZlKGZpbGUucGF0aCgicGxvdHMiLCAic2FtcGxlX2Nvcl9tdXRlY3QyX3ZzX3N0cmVsa2EyLnBkZiIpKQpgYGAKCkxldCdzIGdldCBhIGNvcnJlbGF0aW9uIHRlc3Qgb24gdGhlIGdlbmVzIG92ZXJhbGwuCioqUXVlc3Rpb24gMikqKiBJcyB0aGlzIGEgcmVhc29uYWJsZSBhbW91bnQgb2YgY29uY29yZGFuY2UgZm9yIHRoZXNlIG1ldGhvZHMgCmFjcm9zcyBzYW1wbGVzPyAKCmBgYHtyfQpjb3IudGVzdChjb21iaW5lZF9zYW1wbGUkbXV0ZWN0MiwgY29tYmluZWRfc2FtcGxlJHN0cmVsa2EyLCBtZXRob2QgPSAic3BlYXJtYW4iKQpjb3IudGVzdChjb21iaW5lZF9zYW1wbGUkbXV0ZWN0MiwgY29tYmluZWRfc2FtcGxlJHN0cmVsa2EyLCBtZXRob2QgPSAicGVhcnNvbiIpCmBgYAoKIyMgQ2FsY3VsYXRlIFZBRgoKU2V0IHVwIGRhdGEuZnJhbWUgd2l0aCBtdXRhdGlvbiBhbmQgVkFGIGZvciBTdHJlbGthMi4KCmBgYHtyfQpzdHJlbGthMl92YWYgPC0gc3RyZWxrYTJAZGF0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKCFncmVwbCgiLSIsIEFsbGVsZSkpICU+JQogIGRwbHlyOjptdXRhdGUodmFmID0gYXMubnVtZXJpYyh0X2FsdF9jb3VudCkvKGFzLm51bWVyaWModF9yZWZfY291bnQpICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5udW1lcmljKHRfYWx0X2NvdW50KSksCiAgICAgICAgICAgICAgICBtdXRhdGlvbiA9IHBhc3RlMChIdWdvX1N5bWJvbCwgIl8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFsbGVsZSwgIl8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFR1bW9yX1NhbXBsZV9CYXJjb2RlLCAiXyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnRfUG9zaXRpb24pLCAKICAgICAgICAgICAgICAgIGNoYW5nZSA9IHBhc3RlMChSZWZlcmVuY2VfQWxsZWxlLCAiPiIsIEFsbGVsZSksCiAgICAgICAgICAgICAgICBjb2RpbmcgPSBkcGx5cjo6Y2FzZV93aGVuKCAKICAgICAgICAgICAgICAgICAgQklPVFlQRSAhPSAicHJvdGVpbl9jb2RpbmciIH4gIm5vbi1jb2RpbmciLAogICAgICAgICAgICAgICAgICBUUlVFIH4gInByb3RlaW5fY29kaW5nIikpICU+JQogICAgZHBseXI6OnNlbGVjdCgtd2hpY2goYXBwbHkoaXMubmEoLiksIDIsIGFsbCkpKQoKIyBUYWtlIGEgbG9vayBhdCB0aGlzIGRmCnN0cmVsa2EyX3ZhZgpgYGAKClNldCB1cCBkYXRhLmZyYW1lIHdpdGggbXV0YXRpb24gYW5kIFZBRiBmb3IgTXVUZWN0Mi4KCmBgYHtyfQptdXRlY3QyX3ZhZiA8LSBtdXRlY3QyQGRhdGEgJT4lCiAgZHBseXI6OmZpbHRlcighZ3JlcGwoIi0iLCBBbGxlbGUpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHZhZiA9IGFzLm51bWVyaWModF9hbHRfY291bnQpLyhhcy5udW1lcmljKHRfcmVmX2NvdW50KSArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMubnVtZXJpYyh0X2FsdF9jb3VudCkpLAogICAgICAgICAgICAgICAgbXV0YXRpb24gPSBwYXN0ZTAoSHVnb19TeW1ib2wsICJfIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbGxlbGUsICJfIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUdW1vcl9TYW1wbGVfQmFyY29kZSwgIl8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXJ0X1Bvc2l0aW9uKSwKICAgICAgICAgICAgICAgIGNoYW5nZSA9IHBhc3RlMChSZWZlcmVuY2VfQWxsZWxlLCAiPiIsIEFsbGVsZSksIAogICAgICAgICAgICAgICAgY29kaW5nID0gZHBseXI6OmNhc2Vfd2hlbiggCiAgICAgICAgICAgICAgICAgIEJJT1RZUEUgIT0gInByb3RlaW5fY29kaW5nIiB+ICJub24tY29kaW5nIiwKICAgICAgICAgICAgICAgICAgVFJVRSB+ICJwcm90ZWluX2NvZGluZyIpKSAlPiUKICBkcGx5cjo6c2VsZWN0KC13aGljaChhcHBseShpcy5uYSguKSwgMiwgYWxsKSkpCgojIFRha2UgYSBsb29rIGF0IHRoaXMgZGYKbXV0ZWN0Ml92YWYKYGBgCgpDb21iaW5lIE11VGVjdDIgYW5kIFN0cmVsa2EyIFZBRiBkYXRhLmZyYW1lcyBzbyB3ZSBjYW4gY29tcGFyZS4KCmBgYHtyfQojIE1lcmdlIHRoZXNlIGRhdGEuZnJhbWVzIHRvZ2V0aGVyCnZhZl9kZiA8LSBzdHJlbGthMl92YWYgJT4lCiAgZHBseXI6OmZ1bGxfam9pbihtdXRlY3QyX3ZhZiwgYnkgPSAnbXV0YXRpb24nLCAKICAgICAgICAgICAgICAgICAgICBzdWZmaXggPSBjKCIuc3RyZWxrYTIiLCAiLm11dGVjdDIiKSkgJT4lCiAgIyBNYWtlIGEgdmFyaWFibGUgdGhhdCBkZW5vdGVzIHdoaWNoIGRhdGFzZXQgaXQgaXMgaW4uCiAgZHBseXI6Om11dGF0ZShkYXRhc2V0ID0gZHBseXI6OmNhc2Vfd2hlbiggCiAgICBpcy5uYSh2YWYubXV0ZWN0MikgfiAic3RyZWxrYTJfb25seSIsCiAgICBpcy5uYSh2YWYuc3RyZWxrYTIpIH4gIm11dGVjdDJfb25seSIsIAogICAgVFJVRSB+ICJib3RoIikpIApgYGAKClBsb3QgdGhpcyBhcyBhIHNjYXR0ZXJwbG90CgpgYGB7cn0KdmFmX2RmICU+JQpnZ3Bsb3QyOjpnZ3Bsb3QoZ2dwbG90Mjo6YWVzKHggPSB2YWYuc3RyZWxrYTIsIHkgPSB2YWYubXV0ZWN0MikpICsgCiAgZ2dwbG90Mjo6Z2VvbV9oZXgoKSArCiAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsgCiAgZ2dwbG90Mjo6eGxhYigiVkFGIGZvciBlYWNoIG11dGF0aW9uIGZvciBTdHJlbGthMiIpICsgCiAgZ2dwbG90Mjo6eWxhYigiVkFGIGZvciBlYWNoIG11dGF0aW9uIGZvciBNdVRlY3QyIikgCmBgYAoKYGBge3J9CnZhZl9kZiAlPiUKICB0aWR5cjo6Z2F0aGVyKGtleSA9ICJkYXRhIiwgdmFsdWUgPSAidmFmIiAsIHZhZi5zdHJlbGthMiwgdmFmLm11dGVjdDIpICU+JQogIGRwbHlyOjptdXRhdGUoZGF0YSA9IGdzdWIoIl52YWYuIiwgIiIsIGRhdGEpKSAlPiUgCiAgZHBseXI6Om11dGF0ZShkYXRhLmdyb3VwID0gcGFzdGUoZGF0YXNldCwgIjoiLCBkYXRhLCAiVkFGIikpICU+JQogIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKHZhZikpICU+JQojIFBsb3QgaXQKZ2dwbG90Mjo6Z2dwbG90KGdncGxvdDI6OmFlcyhkYXRhLmdyb3VwLCB2YWYpKSArIAogIGdncGxvdDI6Omdlb21fdmlvbGluKGZpbGwgPSAibGlnaHQgYmx1ZSIpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCApICsgCiAgZ2dwbG90Mjo6eWxhYigiRGVuc2l0eSBvZiBWQUYiKSArIAogIGdncGxvdDI6OnhsYWIoIiAiKQpgYGAKCiMjIFZlbm4gRGlhZ3JhbXMKCk1ha2UgdGhlIFZlbm4gZGlhZ3JhbSBvZiBNdVRlY3QyIGFuZCBTdHJlbGthMiBtdXRhdGlvbnMuIAoKYGBge3J9CmNvdW50IDwtIHN1bW1hcnkoYXMuZmFjdG9yKHZhZl9kZiRkYXRhc2V0KSkKCiMgVGFrZSBhIGxvb2sgYXQgdGhpcyBzdW1tYXJ5CmNvdW50CmBgYAoKYGBge3J9CiMgTWFrZSB0aGUgVmVubiBkaWFncmFtCmdyaWQ6OmdyaWQubmV3cGFnZSgpOwp2ZW5uLnBsb3QgPC0gVmVubkRpYWdyYW06OmRyYXcucGFpcndpc2UudmVubigKICBhcmVhMSA9IGNvdW50WzNdICsgY291bnRbMV0sCiAgYXJlYTIgPSBjb3VudFsyXSArIGNvdW50WzFdLAogIGNyb3NzLmFyZWEgPSBjb3VudFsxXSwKICBjYXRlZ29yeSA9IGMoIlN0cmVsa2EyIiwgIk11VGVjdDIiKSwKICBmaWxsID0gYygiYmx1ZSIsICJ5ZWxsb3ciKSwKICBjZXggPSAyLAogIGNhdC5jZXggPSAxLjUsCiAgY2F0LmRpc3QgPSBjKC0wLjA0LCAtMC4wMzEpLAogIGV4dC5wb3MgPSAwLAogIGV4dC5kaXN0ID0gLTAuMDEsCiAgZXh0Lmxlbmd0aCA9IC44LAogIGV4dC5saW5lLmx3ZCA9IDIsCiAgZXh0LmxpbmUubHR5ID0gImRhc2hlZCIpOwpncmlkOjpncmlkLmRyYXcodmVubi5wbG90KSAjIERyYXcgcGxvdApgYGAKClNhdmUgdGhlIFZlbm4gRGlhZ3JhbSBwbG90IHRvIGEgUERGLiAKCmBgYHtyfQojIE1ha2UgZmlsZW5hbWUgdG8gc2F2ZSBwbG90IGFzCnZlbm4ucGxvdC5maWxlIDwtIGZpbGUucGF0aCgicGxvdHMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJlbGthMl9tdXRlY3QyX3Zlbm5fZGlhZ3JhbS5wZGYiKQpwZGYodmVubi5wbG90LmZpbGUpOwpncmlkOjpncmlkLmRyYXcodmVubi5wbG90KTsKZGV2Lm9mZigpCmBgYAoKIyMgV2hhdCB0eXBlcyBvZiB2YXJpYW50cyBhcmUgYXJlIHRoZSBtb3N0IGRpc2NyZXBhbnQgYmV0d2VlbiB0aGUgYWxnb3JpdGhtcz8KCkxldCdzIG1ha2UgYSB3cmFwcGVyIGZ1bmN0aW9uIHRoYXQgd2lsbCBkbyB0aGlzIGZvciB3aGF0ZXZlciB2YXJpYWJsZXMgd2UgYXJlIAppbnRlcmVzdGVkIGluLiAKCmBgYHtyfQpiYXJwbG90X3ZhciA8LSBmdW5jdGlvbihkZiwgdmFyaWFibGVfbmFtZSA9IE5VTEwsIGZpbHRlcl9jdXRvZmYgPSAxLCAKICAgICAgICAgICAgICAgICAgICAgICAgYXNfcGVyY2VudCA9IEZBTFNFKSB7CiAgY291bnRfZGYgPC0gdmFmX2RmICU+JQogICAgZHBseXI6OnNlbGVjdChwYXN0ZTAodmFyaWFibGVfbmFtZSwgYygiLnN0cmVsa2EyIiwgIi5tdXRlY3QyIikpLAogICAgICAgICAgICAgICAgICBkYXRhc2V0KSAlPiUKICAgIHRpZHlyOjpnYXRoZXIoa2V5ID0gImRhdGEiLCB2YWx1ZSA9ICJ2YXJpYWJsZSIgLCAKICAgICAgICAgICAgICAgICAgcGFzdGUwKHZhcmlhYmxlX25hbWUsIGMoIi5zdHJlbGthMiIsICIubXV0ZWN0MiIpKSkgJT4lCiAgICBkcGx5cjo6bXV0YXRlKCdkYXRhJyA9IGdzdWIocGFzdGUwKHZhcmlhYmxlX25hbWUsICIuIiksICIiLCBkYXRhKSkgCiAgCiAgJT4lCiAgICBkcGx5cjo6Z3JvdXBfYnkoZGF0YXNldCwgZGF0YSwgdmFyaWFibGUpICU+JSAKICAgIGRwbHlyOjpzdW1tYXJpc2UoY291bnQgPSBuKCkpCiAgCiAgdG90YWxzIDwtIGNvdW50X2RmICU+JQogICAgZHBseXI6Omdyb3VwX2J5KGRhdGFzZXQpICU+JQogICAgZHBseXI6OnN1bW1hcmlzZSh0b3RhbCA9IHN1bShjb3VudCwgbmEucm0gPSBUUlVFKSkgCiAgICAKICAgIAogIGNvdW50X2RmICU+JQogICAgZHBseXI6Om11dGF0ZShwZXJjZW50ID0gY291bnRfZGYkY291bnQvdG90YWxzJHRvdGFsW21hdGNoKGNvdW50X2RmJGRhdGFzZXQsIHRvdGFscyRkYXRhc2V0KV0pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgIGRwbHlyOjpmaWx0ZXIoY291bnQgPiBmaWx0ZXJfY3V0b2ZmLCAhaXMubmEodmFyaWFibGUpKSAlPiUKICAgIGRwbHlyOjptdXRhdGUocmVwb3J0ID0gaWZlbHNlKGFzX3BlcmNlbnQsIHBlcmNlbnQsIGNvdW50KSkgJT4lCiAgICBnZ3Bsb3QyOjpnZ3Bsb3QoZ2dwbG90Mjo6YWVzKHggPSByZW9yZGVyKHZhcmlhYmxlLCAtcmVwb3J0KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSByZXBvcnQsIGZpbGwgPSBkYXRhc2V0KSkgKyAKICAgIGdncGxvdDI6Omdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArIAogICAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsKICAgIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC54ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICAgIGNvbG9yYmxpbmRyOjpzY2FsZV9maWxsX09rYWJlSXRvKCkgKyAKICAgIGdncGxvdDI6OnhsYWIoIiIpCn0KYGBgCgpgYGB7cn0KYmFycGxvdF92YXIodmFmX2RmLCAiY2hhbmdlIiwgMTAwKQpgYGAKCmBgYHtyfQpiYXJwbG90X3Zhcih2YWZfZGYsICJjb2RpbmciLCAxMCkKYGBgCgpgYGB7cn0KYmFycGxvdF92YXIodmFmX2RmLCAiQklPVFlQRSIsIDEwKQpgYGAKCmBgYHtyfQpiYXJwbG90X3Zhcih2YWZfZGYsICJWYXJpYW50X0NsYXNzaWZpY2F0aW9uIiwgMTApCmBgYAoKYGBge3J9CmJhcnBsb3RfdmFyKHZhZl9kZiwgIlZBUklBTlRfQ0xBU1MiLCAxMCkKYGBgCgpgYGB7cn0KYmFycGxvdF92YXIodmFmX2RmLCAiSU1QQUNUIiwgMTApCmBgYAoKU2Vzc2lvbiBJbmZvOiAKCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYA==